package com.core.springaop;

import com.core.springaop.observer.AfterObserver;
import com.core.springaop.observer.BeforObserver;
import com.core.springaop.observer.Observer;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.ShadowMatch;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class MyJdkHandle<T> implements InvocationHandler {
    private T t;
    private List<Observer> olist = new ArrayList<>();
    private static MyJdkHandle jdkHandle = null;


    public MyJdkHandle() {

    }

    public void addOserver(Observer o) {
        olist.add(o);
    }

    public static MyJdkHandle getSelf() {
        if (jdkHandle == null) {
            jdkHandle = new MyJdkHandle();
            jdkHandle.addOserver(new BeforObserver());
            jdkHandle.addOserver(new AfterObserver());
        }
        return jdkHandle;
    }

    public void setObject(T t) {
        jdkHandle.t = t;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ismatchBefor(method);
        Object o = method.invoke(t, args);
        //after();
        ismatchAfter(method);
        return o;
    }

    private void ismatchBefor(Method method) {
        Set<String> cutSet = MyAopCache.getBeferAop().keySet();
        for (String cut : cutSet) {
            if (ismatchExpress(method, cut)) {
                Point p = MyAopCache.getBeferAop().get(cut);
                befor(p);
            }
        }
    }

    private void ismatchAfter(Method method) {
        Set<String> cutSet = MyAopCache.getAfterAop().keySet();
        for (String cut : cutSet) {
            if (ismatchExpress(method, cut)) {
                Point p = MyAopCache.getAfterAop().get(cut);
                after(p);
            }
        }
    }

    private boolean ismatchExpress(Method method, String express) {
        PointcutParser pp = PointcutParser
                .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();

        PointcutExpression pe = pp.parsePointcutExpression(express);
        boolean isTypeM = pe.couldMatchJoinPointsInType(method.getDeclaringClass());

        if (isTypeM) {
            ShadowMatch sm = pe.matchesMethodExecution(method);
            return sm.alwaysMatches();
        }
        return false;
    }

    public static boolean ismatchExpressClass(Class<?> clazz, String express) {
        PointcutParser pp = PointcutParser
                .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();

        PointcutExpression pe = pp.parsePointcutExpression(express);

        boolean isTypeM = pe.couldMatchJoinPointsInType(clazz);
        return isTypeM;
    }


    private void befor(Point point) {
        for (Observer o : olist) {
            o.update("befor", point);
        }
    }

    private void after(Point point) {
        for (Observer o : olist) {
            o.update("after", point);
        }
    }
}
